// This is class for Vector 3D
function gewVec3(x, y, z)
{
  this.x = x || 0.0;
  this.y = y || 0.0;
  this.z = z || 0.0;
}
gewVec3.prototype.set = function(x,y,z)
{
  this.x = x;this.y=y;this.z=z;
}
// Dot product
gewVec3.prototype.dot = function(rhs)
{
  return (this.x * rhs.x + this.y * rhs.y + this.z * rhs.z);
}

// Cross product
gewVec3.prototype.cross = function(rhs,result)
{
  if(!result)
    return new gewVec3(this.y * rhs.z - this.z * rhs.y, 
                        this.z * rhs.x - this.x * rhs.z,
                        this.x * rhs.y - this.y * rhs.x);
  else
  {
    result.set(this.y * rhs.z - this.z * rhs.y,this.z * rhs.x - this.x * rhs.z,this.x * rhs.y - this.y * rhs.x);
    return result;
  }
}

// Operator + - * /
gewVec3.prototype.add = function(rhs,result)
{
  if(!result)
    return new gewVec3(this.x + rhs.x, this.y + rhs.y, this.z + rhs.z);
  else
  {
    result.set(this.x + rhs.x, this.y + rhs.y, this.z + rhs.z);
    return result;
  }
}
gewVec3.prototype.sub = function(rhs,result)
{
  if(!result)
    return new gewVec3(this.x - rhs.x, this.y - rhs.y, this.z - rhs.z);
  else
  {
    result.set(this.x - rhs.x, this.y - rhs.y, this.z - rhs.z);
    return result;
  }
}
gewVec3.prototype.mul = function(rhs,result)
{
  if(!result)
    return new gewVec3(this.x * rhs.x, this.y * rhs.y, this.z * rhs.z);
  else
  {
    result.set(this.x * rhs.x, this.y * rhs.y, this.z * rhs.z);
    return result;
  }
}
gewVec3.prototype.div = function(rhs,result)
{
  if(!result)
    return new gewVec3(this.x / rhs.x, this.y / rhs.y, this.z / rhs.z);
  else
  {
    result.set(this.x / rhs.x, this.y / rhs.y, this.z / rhs.z);
    return result;
  }
}
gewVec3.prototype.mulScalar = function(scalar,result) 
{
  if(!result)
    return new gewVec3(this.x * scalar, this.y * scalar, this.z * scalar);
  else
  {
    result.set(this.x * scalar, this.y * scalar, this.z * scalar);
    return result;
  }
}
gewVec3.prototype.divScalar = function(f,result)
{
  if(!result)
    return new gewVec3(this.x/f,this.y/f,this.z/f);
  else
  {
    result.set(this.x/f,this.y/f,this.z/f);
    return result;
  }
}
// Return length of the vector 
gewVec3.prototype.getLength = function()
{
  return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
// Return length^2 of the vector ( this func is faster than getLength! )
gewVec3.prototype.getLengthSquare = function()
{
  return this.x * this.x + this.y * this.y + this.z * this.z;
}
// Compare two vector
gewVec3.prototype.isEqual = function(rhs)
{  
  return gewIsEqual(this.x, rhs.x, GEW_MATH_EPSILON) && 
         gewIsEqual(this.y, rhs.y, GEW_MATH_EPSILON) && 
         gewIsEqual(this.z, rhs.z, GEW_MATH_EPSILON);
}
// Return a copy of this vec3
gewVec3.prototype.dup = function()
{
  return new gewVec3(this.x, this.y, this.z);
}
// Normalize this vector
gewVec3.prototype.normalize = function()
{
  var lensq = this.getLengthSquare();
  if (gewIsZero(lensq, GEW_MATH_EPSILON_SQUARE)) 
  {
    this.x = this.y = this.z = 0.0;
  }
  else if (!gewIsEqual(lensq, 1.0, GEW_MATH_EPSILON_SQUARE))
  {  
    var len = Math.sqrt(lensq);
    this.x /= len;
    this.y /= len;
    this.z /= len;
  }
  return this;
}
// Return angle between two vector ( in radian )
gewVec3.prototype.angleFrom = function(rhs)
{
  var len1 = this.getLength();
  var len2 = rhs.getLength();
  var dot = this.dot(rhs);
  return Math.acos(dot / (len1 * len2));
}

/////////////////////////////////////////////
// Vector 2D
function gewVec2(x, y)
{
  this.x = x || 0.0;
  this.y = y || 0.0;
};
gewVec2.prototype.set = function(x,y)
{
  this.x = x;this.y=y;
};
// Dot product
gewVec2.prototype.dot = function(rhs)
{
  return (this.x * rhs.x + this.y * rhs.y);
};
// Operator + - * /
gewVec2.prototype.add = function(rhs,result)
{
  if(!result)
    result = new gewVec2();
  result.x = this.x + rhs.x;
  result.y = this.y + rhs.y;
  return result;
};
gewVec2.prototype.sub = function(rhs,result)
{
  if(!result)
    result = new gewVec2();
  result.x = this.x - rhs.x;
  result.y = this.y - rhs.y;
  return result;
};
gewVec2.prototype.mul = function(rhs,result)
{
  if(!result)
    result = new gewVec2();
  result.x = this.x * rhs.x;
  result.y = this.y * rhs.y;
};

gewVec2.prototype.div = function(rhs,result)
{
  if(!result)
    result = new gewVec2();
  result.x = this.x / rhs.x;
  result.y = this.y / rhs.y;
};

gewVec2.prototype.mulScalar = function(scalar,result) 
{
  if(!result)
    return new gewVec2(this.x * scalar, this.y * scalar);
  else
  {
    result.set(this.x * scalar, this.y * scalar);
    return result;
  }
}

gewVec2.prototype.divScalar = function(scalar,result) 
{
  if(!result)
    return new gewVec2(this.x/scalar, this.y/scalar);
  else
  {
    result.set(this.x/scalar, this.y/scalar);
    return result;
  }
}
// Return length of the vector 
gewVec2.prototype.getLength = function()
{
  return Math.sqrt(this.x * this.x + this.y * this.y);
};
// Return length^2 of the vector ( this func is faster than getLength! )
gewVec2.prototype.getLengthSquare = function()
{
  return this.x * this.x + this.y * this.y;
};
// Compare two vector
gewVec2.prototype.isEqual = function(rhs)
{
  return gewIsEqual(this.x, rhs.x, GEW_MATH_EPSILON) &&
         gewIsEqual(this.y, rhs.y, GEW_MATH_EPSILON); 
};
// Return a copy of this vector
gewVec2.prototype.dup = function()
{
  return new gewVec2(this.x, this.y);
};
// Normalize this vector
gewVec2.prototype.normalize = function()
{
  var lensq = this.getLengthSquare();
  if (gewIsZero(lensq, GEW_MATH_EPSILON_SQUARE)) 
  {
    this.x = this.y = this.z = 0.0;
  }
  else if (!gewIsEqual(lensq, 1.0, GEW_MATH_EPSILON_SQUARE))
  {
    var len = Math.sqrt(lensq);
    this.x /= len;
    this.y /= len;
  }
  return this;
};
// Return angle between two vector ( in radian )
gewVec2.prototype.angleFrom = function(rhs)
{
  var len1 = this.getLength();
  var len2 = rhs.getLength();
  var dot = this.dot(rhs);
  return Math.acos(dot / (len1 * len2));
};
// Rotate this vector 2D about a point ( clockwise ) , theta in radian
gewVec2.prototype.rotate = function(archor, theta)
{
  var _x = this.x - archor.x;
  var _y = this.y - archor.y;
  var cos0 = Math.cos(theta);
  var sin0 = Math.sin(theta);
  var tempX = _x * cos0 + _y * sin0;
  var tempY = -_x * sin0 + _y * cos0;
  this.x = tempX + archor.x;
  this.y = tempY + archor.y;
  return this;
};

//////////////////////////////////////////////////////
// Vector 4D - current only use as a container
function gewVec4(x, y, z, w)
{
  this.x = x || 0.0;
  this.y = y || 0.0;
  this.z = z || 0.0;
  this.w = w || 0.0;
}
gewVec4.prototype = {
  set:function(x,y,z,w)
  {
    this.x = x;this.y=y;this.z=z;this.w=w;
  }
}

// Operator + - * /
gewVec4.prototype.add = function(rhs,result)
{
  if(!result)
    return new gewVec4(this.x + rhs.x, this.y + rhs.y, this.z + rhs.z, this.w + rhs.w);
  else
  {
    result.set(this.x + rhs.x, this.y + rhs.y, this.z + rhs.z, this.w + rhs.w);
    return result;
  }
}
gewVec4.prototype.sub = function(rhs,result)
{
  if(!result)
    return new gewVec4(this.x - rhs.x, this.y - rhs.y, this.z - rhs.z, this.w - rhs.w);
  else
  {
    result.set(this.x - rhs.x, this.y - rhs.y, this.z - rhs.z, this.w - rhs.wz);
    return result;
  }
}
gewVec4.prototype.mul = function(rhs,result)
{
  if(!result)
    return new gewVec4(this.x * rhs.x, this.y * rhs.y, this.z * rhs.z, this.w * rhs.w);
  else
  {
    result.set(this.x * rhs.x, this.y * rhs.y, this.z * rhs.z, this.w * rhs.w);
    return result;
  }
}
gewVec4.prototype.div = function(rhs,result)
{
  if(!result)
    return new gewVec4(this.x / rhs.x, this.y / rhs.y, this.z / rhs.z, this.w / rhs.w);
  else
  {
    result.set(this.x / rhs.x, this.y / rhs.y, this.z / rhs.z, this.w / rhs.w);
    return result;
  }
}
gewVec4.prototype.mulScalar = function(scalar,result) 
{
  if(!result)
    return new gewVec4(this.x * scalar, this.y * scalar, this.z * scalar, this.w * scalar);
  else
  {
    result.set(this.x * scalar, this.y * scalar, this.z * scalar, this.w * scalar);
    return result;
  }
}
gewVec4.prototype.divScalar = function(f,result)
{
  if(!result)
    return new gewVec4(this.x/f,this.y/f,this.z/f,this.w/f);
  else
  {
    result.set(this.x/f,this.y/f,this.z/f,this.w/f);
    return result;
  }
}